package EA.recording;
import RKUjava.math.RKUGeometry;
import RKUjava.util.*;

/** Implements a number of methods for doing measurements on populations. 
    This could be average fitness, diversity measure, etc.
*/
public class PopulationMeasurements
{

  /** Calculate the average point and store the result in the given array. 
      Time: O(n)<br>
      <b>Note:</b>Each entry in the population array must implement the
      NumericalInspector interface.
      @param population The population to iterate through.
      @param result The array to store the result in.
  */
  public static final void calcAvgPoint(Object[] population, double[] result)
    {
      int i;
      
      RKUArrays.copyArray(((NumericalInspector)population[0]).inspectGetPos(), result);
      
      for (i=1;i<population.length;i++) {
	RKUGeometry.addPoint2Point(result, ((NumericalInspector)population[i]).inspectGetPos());
      }
      RKUGeometry.scalePoint(result, 1.0/population.length);
    }

  /** Calculate the average point and return the point. 
      Time: O(n)<br>
      <b>Note:</b>Each entry in the population array must implement the
      NumericalInspector interface.
      @param population The population to iterate through.
      @return The average point of the population.
  */
  public static final double[] calcAvgPointClone(Object[] population)
    {
      int i;
      double[] res;

      res = RKUArrays.cloneArray(((NumericalInspector)population[0]).inspectGetPos());

      for (i=1;i<population.length;i++) {
	RKUGeometry.addPoint2Point(res, ((NumericalInspector)population[i]).inspectGetPos());
      }
      RKUGeometry.scalePoint(res, 1.0/population.length);

      return res;
    }

  /** Calculate the sum of distance on a population. 
      For each individual is calculated the distance to all other 
      individuals.<br>
      Time: O(n<sup>2</sup>)<br>
      <b>Note:</b>Each entry in the population array must implement the
      NumericalInspector interface.
      @param population The population to iterate though.
      @return The calculated value. If the population is or only contains 1 
      individual then 0 is returned.
  */
  public static final double calcSumDistPopulation(Object[] population)
    {
      int i,j;
      double res = 0;
      double dist;

      if (population.length<2)
	return 0;

      for (i=0;i<population.length;i++) {
	for (j=0;j<population.length;j++) {
	  dist = RKUGeometry.distancePoint2Point(((NumericalInspector)population[i]).inspectGetPos(),((NumericalInspector)population[j]).inspectGetPos());
	  res += dist;
	}
      }
      return res;
    }

  /** Calculate the sum of distance between the best individual
      an the rest of the population. 
      Time: O(n)<br>
      <b>Note:</b>Each entry in the population array must implement the
      NumericalInspector interface.
      @param population The population to iterate though.
      @param ismaximization True if the applied problem is a maximization 
      problem.
      @return The calculated value. If the population is or only contains 1 
      individual then 0 is returned.
  */
  public static final double calcSumDistBest2Population(Object[] population, boolean ismaximization)
    {
      int i;
      int bestindex;
      double res = 0;
      double dist;

      if (population.length<2)
	return 0;

      bestindex = indexOfBest(population, ismaximization);

      for (i=0;i<population.length;i++) {
	dist = RKUGeometry.distancePoint2Point(((NumericalInspector)population[bestindex]).inspectGetPos(),((NumericalInspector)population[i]).inspectGetPos());
	res += dist;
      }
      return res;
    }

  /** Calculate the average distance between all the individuals in the 
      population.
      Time: O(n<sup>2</sup>)<br>
      <b>Note:</b>Each entry in the population array must implement the
      NumericalInspector interface.
      @param population The population to iterate though.
      @return The calculated value. If the population is empty or only
      contains 1 individual then 0 is returned.

  */
  public static final double calcAvgDistPopulation(Object[] population)
    {
      if (population.length<2)
	return 0;
      else
	return calcSumDistPopulation(population)/((population.length-1)*(population.length-1));
    }

  /** Calculate the average distance between the best individual and the 
      rest of the population. 
      <b>Note:</b>Each entry in the population array must implement the
      NumericalInspector interface.
      @param population The population to iterate though.
      @param ismaximization True if the applied problem is a maximization 
      problem.
      @return The calculated value. If the population is empty or only
      contains 1 individual then 0 is returned.
  */
  public static final double calcAvgDistBest2Population(Object[] population, boolean ismaximization)
    {
      if (population.length<2)
	return 0;
      else
	return calcSumDistBest2Population(population, ismaximization)/(population.length-1);
    };

  /** Calculate the mean fitness of the population.
      Time: O(n)<br>
      <b>Note:</b>Each entry in the population array must implement the
      NumericalInspector interface.
      @param population The population to iterate though.
      @return The mean of the fitness.
  */
  public static final double calcMean(Object[] population)
    {
      int i;
      double res = 0;
      double dist;

      for (i=0;i<population.length;i++) {
	dist = ((NumericalInspector)population[i]).inspectGetFitness();
	res += dist;
      }

      return res/population.length;
    }

  /** Calculate the standard fitness deviation (variance) used in normal 
      distributions. <br>
      The formula is: sqrt(sum((x_mean - x_i)<sup>2</sup>)/(n-1)), 
      where x_mean is the mean of the individuals, x_i is the fitness of the
      i'th individual and n is the size of the population.<br>
      Time: O(n)<br>
      <b>Note:</b>Each entry in the population array must implement the
      NumericalInspector interface.
      @param population The population to iterate though. 
      @return The deviation of the fitness related to the average fitness. Returns 0 if the popsize is 0 or 1.
  */
  public static final double calcStandardDeviation(Object[] population)
    {
      int i;
      double res = 0;
      double mean;
      double dist;

      if (population.length<2)
	return 0;

      mean = calcMean(population);

      for (i=0;i<population.length;i++) {
	dist = mean - ((NumericalInspector)population[i]).inspectGetFitness();
	res += dist*dist;
      }
      return Math.sqrt(res/(population.length-1));
    }

  /** Calculate the standard error used in connection with normal 
      distributions.<br>
      Formula: sqrt(SD), where SD is the standard deviation and n is 
      the size of the population.<br>
      Time: O(n)<br>
      <b>Note:</b>Each entry in the population array must implement the
      NumericalInspector interface.
      @param population The population to iterate though. 
      @return The deviation of the fitness related to the average fitness. 
      Returns 0 if the popsize is 0 or 1.
  */
  public static final double calcStandardError(Object[] population)
    {
      if (population.length<2)
	return 0;
      else
	return Math.sqrt(calcStandardDeviation(population));
    }

  /** Find the index of the best individual in the population.
      @param population The population to iterate though.
      @param ismaximization True if the applied problem is a maximization 
      problem.
      @return The index or -1 if population has length 0;
  */
  public static final int indexOfBest(Object[] population, boolean ismaximization)
    {
      int i, bestindex = -1;

      if (ismaximization) {
	for (i=0;i<population.length;i++) {
	  if (bestindex==-1)
	    bestindex = i;
	  else
	    if (((NumericalInspector)population[bestindex]).inspectGetFitness()<((NumericalInspector)population[i]).inspectGetFitness())
	      bestindex = i;
	}
      }
      else {
	for (i=0;i<population.length;i++) {
	  if (bestindex==-1)
	    bestindex = i;
	  else
	    if (((NumericalInspector)population[bestindex]).inspectGetFitness()>((NumericalInspector)population[i]).inspectGetFitness())
	      bestindex = i;
	}
      }
      return bestindex;
    }

  /** Find the index of the worst individual in the population.
      @param population The population to iterate though.
      @param ismaximization True if the applied problem is a maximization 
      problem.
      @return The index or -1 if population has length 0;
  */
  public static final int indexOfWorst(Object[] population, boolean ismaximization)
    {
      return indexOfBest(population, !ismaximization);
    }


}
